//
// Created by Soar Qin on 2017/8/18.
// Copyright (c) 2017 Soar Qin. All rights reserved.
//

#include "common.hh"

#include "timer.hh"

#include "core/async/private/io_event_private.hh"

#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/time.h>
#endif

namespace core::async {

struct TimerContext {
    static evutil_monotonic_timer *mtimer;
    struct timeval next = {};
    struct timeval repeat = {};
};

evutil_monotonic_timer *TimerContext::mtimer = []() {
    evutil_monotonic_timer *mtimer = evutil_monotonic_timer_new();
    // evutil_configure_monotonic_time(mtimer, EV_MONOT_PRECISE);
    return mtimer;
}();

Timer::Timer(Loop &loop): IoEvent(loop), timer_(new TimerContext) {
}

Timer::~Timer() {
    delete timer_;
}

bool Timer::start(uint64_t interval, uint64_t repeat) {
    if (!IoEvent::startInternal(-1, 0)) return false;
    evutil_gettime_monotonic(timer_->mtimer, &timer_->next);
    struct timeval toadd = {static_cast<long>(interval / 1000000), static_cast<long>(interval % 1000000)};
    evutil_timeradd(&timer_->next, &toadd, &timer_->next);
    timer_->repeat = {static_cast<long>(repeat / 1000000), static_cast<long>(repeat % 1000000)};
    if (event_add(context_->ev, &toadd) < 0) {
        event_free(context_->ev);
        context_->ev = nullptr;
        context_->fd = -1;
        context_->enabled = 0;
        return false;
    }
    return true;
}

void Timer::stop() {
    if (context_->ev != nullptr)
        event_del(context_->ev);
}

bool Timer::restart(uint64_t interval, uint64_t repeat) {
    if (context_->ev == nullptr && !IoEvent::startInternal(-1, 0)) return false;
    struct timeval toadd = {static_cast<long>(interval / 1000000), static_cast<long>(interval % 1000000)};
    evutil_gettime_monotonic(timer_->mtimer, &timer_->next);
    evutil_timeradd(&timer_->next, &toadd, &timer_->next);
    timer_->repeat = {static_cast<long>(repeat / 1000000), static_cast<long>(repeat % 1000000)};
    event_add(context_->ev, &toadd);
    return true;
}

void Timer::onEvent(short events) {
    if (timer_->repeat.tv_sec == 0 && timer_->repeat.tv_usec == 0) {
        onTimer();
        return;
    }
    struct timeval now, timediff;
    evutil_timeradd(&timer_->next, &timer_->repeat, &timer_->next);
    evutil_gettime_monotonic(timer_->mtimer, &now);
    evutil_timersub(&timer_->next, &now, &timediff);
    if (timediff.tv_sec < 0)
        timediff.tv_sec = timediff.tv_usec = 0;
    event_add(context_->ev, &timediff);
    onTimer();
}

}
